<!DOCTYPE html>
<html>

<head>
    <title>音乐小工具</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta charset="UTF-8" />
    <script type="text/javascript" src="js/phaser.js"></script>
    <script type="text/javascript" src="js/main.js"></script>
    <script type="text/javascript" src="js/chord_parser.js"></script>
</head>
<style>
    .control {
        margin: auto;
        width: 90%;
        padding: 10px;
        font-size: 25px;
        text-align: center;
    }

    input {
        height: 25px;
        font-size: 25px;
    }

    select {
        font-size: 25px;
    }

    .button {
        width: 90%;
        font-size: 25px;
        margin: 1em;
    }

    .big {
        font-size: 40px;
        color: lightskyblue;
    }
</style>

<body>
    <div id="gameDiv"> </div>
    <div class="control">
        半音间隔 <input type="number" value="1" min="1" max="12" id="input_step" onchange="show_interval_name()" />
        <div id="div_step_name" class="big"></div>
        <button id="btn_play" class="button" onclick="play_interval()">循环播放音程</button>
    </div>

    <div id="chord" class="control">

        <select id="note-range">
            <option value="1">音区1</option>
            <option value="2">音区2</option>
            <option value="3">音区3</option>
            <option value="4">音区4</option>
            <option value="5">音区5</option>
            <option value="6">音区6</option>
        </select>

        <select id="root-note">
        </select>

        <select id="chord-select">
            <option value="maj3">大三和弦</option>
            <option value="min3">小三和弦</option>
            <option value="maj7">大七和弦</option>
            <option value="min7">小七和弦</option>
            <option value="dom7">属七和弦</option>
        </select>

        <input type="checkbox" id="arpeggio"/> <label for="arpeggio">播放琶音</label>

        <br/>
        <button id="btn_play_chord" class="button" onclick="play_chord()">播放和弦</button> <br/>
    </div>

    <div class="control">
        输入和弦： <input id="input_chord" onkeydown="on_input_chord_keydown(event)" /> <br/>
        <button class="button" onclick="on_change_input_chord()">播放输入的和弦</button>
        <div id="chord-detail" class="big"></div>
    </div>
    <script lang="javascript">
        const inputStep = document.getElementById("input_step");
        const btnPlay = document.getElementById("btn_play");
        const divStepName = document.getElementById("div_step_name");
        var playing = false;
        var all_note_names = null;

        var stepNameMap = {
            "1": "小二度",
            "2": "大二度",
            "3": "小三度",
            "4": "大三度",
            "5": "纯四度",
            "6": "增四度 - 减五度",
            "7": "纯五度",
            "8": "小六度",
            "9": "大六度",
            "10": "小七度",
            "11": "大七度",
            "12": "纯八度"
        };

        /**
         * Play a tune sequence.
         *
         * @param {Array} tunes - [{"note": 32, "length": 1000}, {"note": 33, "length": 500}]
         * @param {function} onfinish - the callback when all notes are finished
         */
        function play_interval_sequence(tunes, onfinish) {
            function play_interval_internal(tunes, index) {
                var item = tunes[index];
                if (Array.isArray(item.note)) {
                    item.note.forEach((n) => {
                        play_note(n);
                    })
                } else {
                    play_note(item.note);
                }
                if (index < tunes.length - 1) {
                    setTimeout(function () {
                        play_interval_internal(tunes, index + 1);
                    }, item.length);
                } else {
                    setTimeout(onfinish, item.length);
                }
            }
            play_interval_internal(tunes, 0);
        }

        function play_interval() {
            playing = !playing;

            function play_random_sequence() {
                if (!playing)
                    return;

                var step = parseInt(inputStep.value, 10);
                var start = parseInt(Math.random() * 88, 10) + 1;
                var end = start + step;
                // avoid out of range
                if (end > 88) {
                    end = 88;
                    start = end - step;
                }

                var notes = [
                    { note: start, length: 1000 },
                    { note: start, length: 500 },
                    { note: end, length: 500 },
                    { note: start, length: 1000 },
                    { note: [start, end], length: 2000 }
                ];

                play_interval_sequence(notes, play_random_sequence);
            }

            if (playing) {
                btnPlay.innerText = "停止";
                play_random_sequence();
            } else {
                btnPlay.innerText = "播放";
            }
        }

        function show_interval_name() {
            var step = inputStep.value;
            var text = stepNameMap[step];
            divStepName.innerText = text;
        }

        // 产生大三和弦
        function gen_chord_maj3(start_note) {
            let end_note = start_note + 7;  // 纯五度
            let middle_note = start_note + 4;   // 大三度
            return [start_note, middle_note, end_note];
        }
        
        // 产生小三和弦
        function gen_chord_min3(start_note) {
            let end_note = start_note + 7;  // 纯五度
            let middle_note = start_note + 3;   // 小三度
            return [start_note, middle_note, end_note];
        }

        // 产生大七和弦
        function gen_chord_maj7(start_note) {
            let end_note = start_note + 11;  // 大七度
            let middle_note1 = start_note + 4;   // 大三度
            let middle_note2 = start_note + 7;   // 纯五度
            return [start_note, middle_note1, middle_note2, end_note];
        }
        
        // 产生小七和弦
        function gen_chord_min7(start_note) {
            let end_note = start_note + 10;  // 小七度
            let middle_note1 = start_note + 3;   // 小三度
            let middle_note2 = start_note + 7;   // 纯五度
            return [start_note, middle_note1, middle_note2, end_note];
        }

        // 产生属七和弦
        function gen_chord_dom7(start_note) {
            let end_note = start_note + 10;  // 小七度
            let middle_note1 = start_note + 4;   // 大三度
            let middle_note2 = start_note + 7;   // 纯五度
            return [start_note, middle_note1, middle_note2, end_note];
        }

        // 产生6和弦
        function gen_chord_add6(start_note) {
            let third_note = start_note + 7;  // 纯五度
            let second_note = start_note + 4;   // 大三度
            let fourth_note = start_note + 9;   // 大六度
            return [start_note, second_note, third_note, fourth_note];
        }

        // 产生九和弦
        function gen_chord_9(start_note) {
            let second_note = start_note + 4;   // 大三度
            let third_note = start_note + 7;    // 纯五度
            let fourth_note = start_note + 10;  // 小七度
            let fifth_note = start_note + 14;   // 大九度
            return [start_note, second_note, third_note, fourth_note, fifth_note];
        }

        // 产生加九音和弦
        function gen_chord_add9(start_note) {
            let second_note = start_note + 4;   // 大三度
            let third_note = start_note + 7;    // 纯五度
            let fourth_note = start_note + 2;   // 大二度，也就是大九度
            return [start_note, second_note, third_note, fourth_note];
        }

        // 产生大七升五和弦
        function gen_chord_maj7_aug5(start_note) {
            let second_note = start_note + 4;   // 大三度
            let third_note = start_note + 7;    // 纯五度
            let fourth_note = start_note + 11;  // 大七度
            let fifth_note = start_note + 8;    // 增五度
            return [start_note, second_note, third_note, fourth_note, fifth_note];
        }

        let btn_play_chord = document.getElementById("btn_play_chord");
        
        function disable_chord_buttons() {
            btn_play_chord.disabled = true;
        }

        function enable_chord_buttons() {
            btn_play_chord.disabled = false;
        }

        function play_chord() {
            play_chord_again()
        }

        function play_chord() {
            // 找到根音
            let s = document.getElementById("root-note");
            let root_note = s.options[s.selectedIndex].innerHTML;

            s = document.getElementById("note-range");
            let range = s.options[s.selectedIndex].value;
            let chord_start = get_root_note(root_note, range);
            // 判断和弦类型
            s = document.getElementById("chord-select");
            let chord_type = s.options[s.selectedIndex].value;
           
            // 播放琶音
            let arpeggio = document.getElementById("arpeggio").checked;
            play_chord_internal(chord_start, chord_type, arpeggio);
        }

        /**
         * @param {int} chord_start: value between 1 and 88
         * @param {string} chord_type: chord type "maj3", "min3"...
         * @param {boolean} arpeggio: play arpeggio before chord.
         */
        function play_chord_internal(chord_start, chord_type, arpeggio) {
            
            let chord_function_map = {
                "maj3": gen_chord_maj3,
                "min3": gen_chord_min3,
                "maj7": gen_chord_maj7,
                "min7": gen_chord_min7,
                "dom7": gen_chord_dom7
            };
            let f = chord_function_map[chord_type];
            let notes = f(chord_start);
            let sequence = [];

            if (arpeggio) {
                notes.forEach((x) => {
                    sequence.push({note: x, length: 500})
                })
                // 停顿一秒，然后播放和弦
                sequence.push({note:0, length: 1000});
            }
            sequence.push({note: notes, length: 2000})
            play_interval_sequence(sequence, function(){});
        }

        function gen_note_name_map() {
            let names = [""];
            ["A", "A#/Bb", "B"].forEach((x) => {names.push(x)})
            let note_names = ["C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab", "A", "A#/Bb", "B"];
            let select_root_note = document.getElementById("root-note");
            select_root_note.innerHTML = "";
            note_names.forEach((x) => {
                let s = document.createElement("option");
                s.innerHTML = x;
                select_root_note.appendChild(s);
            })
            for (let i = 0; i < 7; i++) {
                note_names.forEach((x) => {
                    names.push(x);
                })
            }
            names.push("C");
            all_note_names = names;
        }

        /**
         * Get root note of a chord.
         * 
         * @param {string} name - the name of the note, like "C" or "C#"
         * @param {int} range - the range of the note, number 1 to 6
         * 
         * @return {int} the index of note, a number between 1 and 88
         */
        function get_root_note(name, range) {
            let start = 15 + 12 * (parseInt(range)-1);
            for (let i = start; i <= 88; i++) {
                let note_name = all_note_names[i];
                if (note_name == name) {
                    return i;
                }
            }
            return 0;
        }

        function handle_key_down(event) {
            let keycode = event.keyCode;
            // 找到根音
            let s = document.getElementById("root-note");
            let root_note = s.options[s.selectedIndex].innerHTML;

            s = document.getElementById("note-range");
            let range = s.options[s.selectedIndex].value;
            let tone_start = get_root_note(root_note, range);

            switch (keycode) {
                case 49:
                    // '1'
                    play_chord_internal(tone_start, 'maj3', false);
                    break;
                case 50:
                    // '2'
                    play_chord_internal(tone_start + 2, 'min3', false);
                    break;
                case 51:
                    // '3'
                    play_chord_internal(tone_start + 4, 'min3', false);
                    break;
                case 52:
                    // '4'
                    play_chord_internal(tone_start + 5, 'maj3', false);
                    break;
                case 53:
                    // '5'
                    play_chord_internal(tone_start + 7, 'maj3', false);
                    break;
                case 54:
                    // '6'
                    play_chord_internal(tone_start + 9, 'min3', false);
                    break;
                case 55:
                    // '7'
                    play_chord_internal(tone_start + 7, 'dom7', false);
                    break;
            }
        }

        function on_change_input_chord() {
            let input_chord = document.getElementById("input_chord");
            let chord_name = input_chord.value;
            let s = document.getElementById("note-range");
            let range = s.options[s.selectedIndex].value;

            try {
                let c = build_chord(chord_name, range);
                if (c != null) {
                    console.log(c.toString());
                    let notes = c.get_notes();
                    let notes_str = notes.map(x => NoteResolver.get_note_name(x)).join(" ");
                    let chord_detail = document.getElementById("chord-detail");
                    console.log(notes_str);

                    chord_detail.innerText = notes_str;
                    let sequence = [];

                    let arpeggio = document.getElementById("arpeggio").checked;
                    if (arpeggio) {
                        notes.forEach((x) => {
                            sequence.push({note: x, length: 500})
                        })
                        // 停顿一秒，然后播放和弦
                        sequence.push({note:0, length: 1000});
                    }
                    sequence.push({note: notes, length: 2000})
                    play_interval_sequence(sequence, function(){});
                }
            } catch (e) {
                console.log(e);
            }
        }

        function on_input_chord_keydown(e) {
            let key = e.keyCode;
            if (key == 13 || key == 32) {
                on_change_input_chord();
            }
        }

        gen_note_name_map();
        show_interval_name();
        //document.onkeydown = handle_key_down;
    </script>
</body>

</html>
